package main

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/go-chi/chi"
)

const (
	dadataUrl = "https://dadata.ru/api/v2/"
	apiToken  = "b7bbd64b04ac588df342f5a7df4aeb2d36c7e591"
	apiSecret = "68fa60140c8e34ffd6bac13f3e50db09df4da0b2"
)

// Интерфейс для клиента DaData
type DaDataClientInterface interface {
	searchAddress(query string) ([]*Address, error)
	geocodeAddress(lat, lng string) ([]*Address, error)
}

// Структуры запросов и ответов
type RequestAddressSearch struct {
	Query string `json:"query"`
}

type RequestAddressGeocode struct {
	Lat string `json:"lat"`
	Lng string `json:"lng"`
}

type ResponseAddress struct {
	Addresses []*Address `json:"addresses"`
}
type Address struct {
	Value string `json:"value"`
	City  string `json:"city"`
}

type DaDataClient struct {
	ApiURL   string
	ApiToken string
	Client   *http.Client
}

func NewDaDataClient() *DaDataClient {
	return &DaDataClient{
		ApiURL:   dadataUrl,
		ApiToken: apiToken,
		Client:   &http.Client{},
	}
}

// Функция для отправки запросов к DaData
func (c *DaDataClient) sendDaDataRequest(url string, body []byte) (*http.Response, error) {
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", fmt.Sprintf("Token %s", c.ApiToken))

	return c.Client.Do(req)
}

// Реализация метода поиска адреса для реального клиента
func (c *DaDataClient) searchAddress(query string) ([]*Address, error) {
	url := c.ApiURL + "suggest/address"
	body, err := json.Marshal(map[string]string{"query": query})
	if err != nil {
		return nil, err
	}

	resp, err := c.sendDaDataRequest(url, body)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errors.New("ошибка DaData")
	}

	var data struct {
		Suggestions []struct {
			Value string `json:"value"`
			Data  struct {
				City string `json:"city"`
			} `json:"data"`
		} `json:"suggestions"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}

	var addresses []*Address
	for _, suggestion := range data.Suggestions {
		addresses = append(addresses, &Address{
			Value: suggestion.Value,
			City:  suggestion.Data.City,
		})
	}

	return addresses, nil
}

// Реализация метода геокодирования для реального клиента
func (c *DaDataClient) geocodeAddress(lat, lng string) ([]*Address, error) {
	url := c.ApiURL + "geolocate/address"
	body, err := json.Marshal(map[string]string{"lat": lat, "lon": lng})
	if err != nil {
		return nil, err
	}

	resp, err := c.sendDaDataRequest(url, body)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errors.New("ошибка DaData")
	}

	var data struct {
		Suggestions []struct {
			Value string `json:"value"`
			Data  struct {
				City string `json:"city"`
			} `json:"data"`
		} `json:"suggestions"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}

	var addresses []*Address
	for _, suggestion := range data.Suggestions {
		addresses = append(addresses, &Address{
			Value: suggestion.Value,
			City:  suggestion.Data.City,
		})
	}

	return addresses, nil
}

// Обработчик для поиска по адресу
func handleAddressSearch(client DaDataClientInterface) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req RequestAddressSearch
		err := json.NewDecoder(r.Body).Decode(&req)
		if err != nil || req.Query == "" {
			http.Error(w, "Неверный формат запроса", http.StatusBadRequest)
			return
		}

		// Запрос к DaData
		addresses, err := client.searchAddress(req.Query)
		if err != nil {
			http.Error(w, "Ошибка сервиса DaData", http.StatusInternalServerError)
			return
		}

		resp := ResponseAddress{
			Addresses: addresses,
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(resp)
	}
}

// Обработчик для геокодирования
func handleAddressGeocode(client DaDataClientInterface) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req RequestAddressGeocode
		err := json.NewDecoder(r.Body).Decode(&req)
		if err != nil || req.Lat == "" || req.Lng == "" {
			http.Error(w, "Неверный формат запроса", http.StatusBadRequest)
			return
		}

		// Запрос к DaData для геокодирования
		addresses, err := client.geocodeAddress(req.Lat, req.Lng)
		if err != nil {
			http.Error(w, "Ошибка сервиса DaData", http.StatusInternalServerError)
			return
		}

		resp := ResponseAddress{
			Addresses: addresses,
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(resp)
	}
}

func main() {
	daDataClient := NewDaDataClient()

	router := chi.NewRouter()
	router.Post("/api/address/search", handleAddressSearch(daDataClient))
	router.Post("/api/address/geocode", handleAddressGeocode(daDataClient))

	port := ":8080"
	server := &http.Server{
		Addr:         port,
		Handler:      router,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	go func() {
		log.Printf("Запуск сервера на порту %s", port)
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("Server error: %v", err)
		}
	}()

	sigChannel := make(chan os.Signal, 1)
	signal.Notify(sigChannel, syscall.SIGINT, syscall.SIGTERM)

	<-sigChannel
	log.Println("Пни меня нежно")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := server.Shutdown(ctx); err != nil {
		log.Fatalf("Server shutdown error: %v", err)
	}

	log.Println("Server stopped gracefully")
}
